package cn.lingyangwl.agile.infra.file.channel.aliyun;

import cn.hutool.core.util.RandomUtil;
import cn.lingyangwl.agile.infra.file.channel.AbstractStorageChannel;
import cn.lingyangwl.agile.model.enums.StorageTypeEnum;
import cn.lingyangwl.agile.model.module.config.param.StorageParam;
import cn.lingyangwl.agile.model.module.config.param.storage.AliyunStorageParam;
import cn.lingyangwl.agile.model.module.tenant.TenantContext;
import cn.lingyangwl.framework.tool.core.StringUtils;
import cn.lingyangwl.framework.tool.core.UrlUtils;
import cn.lingyangwl.framework.tool.core.exception.Assert;
import cn.lingyangwl.framework.tool.core.exception.BizException;
import cn.lingyangwl.framework.tool.core.file.FileUtils;
import cn.lingyangwl.framework.tool.core.file.MimeTypesUtils;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.List;
import java.util.function.Consumer;

/**
 * @author shenguangyang
 */
@Slf4j
public class AliyunStorageChannel extends AbstractStorageChannel {
    /**
     * 批量删除组大小
     */
    private static final Integer DELETE_BATCH_GROUP_SIZE = 900;

    private AliyunStorageParam param;
    
    private OssUtils ossUtils;

    @Override
    public StorageTypeEnum storageType() {
        return StorageTypeEnum.ALIYUN;
    }

    @Override
    public void createClient(StorageParam<?> storageParam) throws Exception {
        this.param = (AliyunStorageParam) storageParam;
        log.info("aliyun oss ===> create client, tenantId: {}", TenantContext.get().getTenantId());
        String endpoint = this.param.getEndpoint();
        OSS ossClient = new OSSClientBuilder().build(endpoint, this.param.getAccessKey(), this.param.getSecretKey());

        String bucketName = this.param.getBucket();
        boolean exists = ossClient.doesBucketExist(bucketName);
        //判定是否存在此存储空间
        if (!exists) {
            log.info("aliyun oss ===> create bucket: {}", bucketName);
            ossClient.createBucket(bucketName);
        } else {
            log.info("aliyun oss ===> bucket {} exist", bucketName);
        }
        
        this.ossUtils = new OssUtils(ossClient);
        log.info("aliyun oss ===> init success");
    }

    @Override
    public String getIntranetUrl(String bucketName, String objectName) {
        String endpoint = this.param.getEndpoint();
        if (endpoint.startsWith("https://")) {
            String url = endpoint.replace("https://", "https://" + bucketName + ".") + "/" + objectName;
            return UrlUtils.removeRepeatSlashOfUrl(url);
        } else if (endpoint.startsWith("http://")) {
            String url = endpoint.replace("http://", "http://" + bucketName + ".") + "/" + objectName;
            return UrlUtils.removeRepeatSlashOfUrl(url);
        } else {
            throw new BizException("aliyun 端点地址不是以http(s)://开头, 请检查");
        }
    }

    @Override
    public void uploadText(String text, String objectName) {
        String bucketName = param.getBucket();
        InputStream inputStream = new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8));
        this.ossUtils.upload(inputStream, bucketName, objectName, "text/plain");
    }

    @Override
    public void uploadFile(InputStream inputStream, String contentType, String objectName) throws Exception {
        String bucketName = this.param.getBucket();
        this.ossUtils.upload(inputStream, bucketName, objectName, contentType);
    }

    @Override
    public void uploadDir(String dirPath) {
        List<String> allFileList = FileUtils.getAllFile(dirPath, false, null);
        dirPath = dirPath.replace("\\", "/");

        if (!dirPath.endsWith("/")) {
            dirPath = dirPath + "/";
        }

        for (String filePath : allFileList) {
            String path = filePath.replace("\\", "/").replace(dirPath, "");

            // 针对win路径进行处理
            if (path.contains(":")) {
                path = path.substring(path.lastIndexOf(":") + 1);
            }

            String objectName = path;
            try (InputStream inputStream = Files.newInputStream(Paths.get(filePath))) {
                uploadFile(inputStream, MimeTypesUtils.getInstance().getMimetype(FileUtils.getName(filePath)), objectName);
            } catch (Exception e) {
                log.error("error: ", e);
            }
        }
    }

    @Override
    public String getObjectUrl(String objectName) {
        String bucketName = this.param.getBucket();
        String url = this.ossUtils.getAccessURL(bucketName, objectName);
        Assert.notEmpty(url, "获取url失败");
        return url;
    }

    @Override
    public InputStream getFile(String objectName) {
        String bucketName = this.param.getBucket();
        return this.ossUtils.getFile(bucketName, objectName);
    }

    @Override
    public List<String> listFilePath(String pathPrefix) {
        String bucketName = this.param.getBucket();

        // 移除前后两个 /
        pathPrefix = StringUtils.removeEnd(pathPrefix, "/");
        pathPrefix = StringUtils.removeStart(pathPrefix, "/");
        return this.ossUtils.getFilePathList(bucketName, pathPrefix, null);
    }

    @Override
    public List<String> listFilePath(String pathPrefix, Consumer<String> filePathCallback) {
        String bucketName = this.param.getBucket();

        // 移除前后两个 /
        pathPrefix = StringUtils.removeEnd(pathPrefix, "/");
        pathPrefix = StringUtils.removeStart(pathPrefix, "/");
        return this.ossUtils.getFilePathList(bucketName, pathPrefix, filePathCallback);
    }

    @Override
    public Boolean createBucket(Boolean randomSuffix) {
        String bucketName = this.param.getBucket();
        try {
            if (randomSuffix != null && randomSuffix) {
                return this.ossUtils.createBucket(bucketName + "-" + RandomUtil.randomString(8));
            }
            return this.ossUtils.createBucket(bucketName);
        } catch (Exception e) {
            log.error("createBucket::bucketName = [{}] message = {}", bucketName, e.getMessage());
            return false;
        }
    }

    @Override
    public Boolean createBucket(String bucketName) {
        try {
            return this.ossUtils.createBucket(bucketName);
        } catch (Exception e) {
            log.error("createBucket::bucketName = [{}] message = {}", bucketName, e.getMessage());
            return false;
        }
    }

    @Override
    public Boolean doesBucketExist(String bucketName) {
        try {
            return this.ossUtils.doesBucketExist(bucketName);
        } catch (Exception e) {
            log.error("doesBucketExist::bucketName = [{}] message = {}", bucketName, e.getMessage());
            return false;
        }
    }

    /**
     * 一次最大删除900左右文件
     *
     * @param objectNameList 对象名集合
     * @throws BizException 异常
     */
    @Override
    public void deleteObjects(List<String> objectNameList) throws BizException {
        String bucketName = this.param.getBucket();
        for (List<String> groupObjectNames : Lists.partition(objectNameList, DELETE_BATCH_GROUP_SIZE)) {
            int deleteSize = this.ossUtils.deleteObjects(bucketName, groupObjectNames);
            if (deleteSize != groupObjectNames.size()) {
                throw new BizException("实际删除的文件数量: " + deleteSize + ", 目标删除文件数量: " + groupObjectNames.size());
            }
        }
    }

    @Override
    public void deleteObjects(String bucketName, List<String> objectNameList) throws Exception {
        for (List<String> groupObjectNames : Lists.partition(objectNameList, DELETE_BATCH_GROUP_SIZE)) {
            int deleteSize = this.ossUtils.deleteObjects(bucketName, groupObjectNames);
            if (deleteSize != groupObjectNames.size()) {
                throw new BizException("实际删除的文件数量: " + deleteSize + ", 目标删除文件数量: " + groupObjectNames.size());
            }
        }
    }

    @Override
    public void deleteObject(String objectName) throws Exception {
        String bucketName = this.param.getBucket();
        this.ossUtils.deleteObject(bucketName, objectName);
    }

    @Override
    public void deleteObject(String bucketName, String objectName) throws Exception {
        this.ossUtils.deleteObject(bucketName, objectName);
    }

    @Override
    public boolean checkFileIsExist(String objectName) {
        String bucketName = this.param.getBucket();
        return this.ossUtils.objectExist(bucketName, objectName);
    }

    @Override
    public boolean checkFolderIsExist(String folderName) {
        return super.checkFolderIsExist(folderName);
    }
}
